using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Microsoft.Data.Schema.Extensibility;
using Microsoft.Data.Schema.SchemaModel;
using Microsoft.Data.Schema.ScriptDom.Sql;
using Microsoft.Data.Schema.Sql.SchemaModel;
using Microsoft.Data.Schema.Sql;
using Microsoft.Data.Schema.StaticCodeAnalysis;
using System.IO;
using System.Text.RegularExpressions;

namespace Neznayka
{
    /// <summary>
    /// This is a SQL rule which returns a warning message 
    /// whenever it finds a variable name that is too short.
    /// </summary>

    [DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))]
    [DataRuleAttribute(
        NeznaykaConstants.NameSpace,
        NeznaykaConstants.VariableNameLengthRuleId,
        NeznaykaConstants.ResourceBaseName,
        NeznaykaConstants.VariableNameLength_RuleName,
        NeznaykaConstants.CategoryVariableNaming,
        DescriptionResourceId = NeznaykaConstants.VariableNameLength_ProblemDescription)]
    [SupportedElementType(typeof(ISqlFunction))]
    [SupportedElementType(typeof(ISqlTableValuedFunction))]
    [SupportedElementType(typeof(ISqlMultiStatementTableValuedFunction))]
    [SupportedElementType(typeof(ISqlProcedure))]
    [SupportedElementType(typeof(ISqlTrigger))]
    public class VariableNameLengthRule : StaticCodeAnalysisRule
    {
        #region Overrides
        /// <summary>
        /// Analyze the model element
        /// </summary>
        public override IList<DataRuleProblem> Analyze(DataRuleSetting ruleSetting, DataRuleExecutionContext context)
        {
            // (Re)-Load Environment settings

            List<DataRuleProblem> problems;
            SqlSchemaModel sqlSchemaModel;
            ISqlModelElement sqlElement;
            TSqlFragment sqlFragment;
            DMVRuleSetup.RuleSetup(context, out problems, out sqlSchemaModel, out sqlElement, out sqlFragment);

            // visitor to get the ocurrences of  Whiffy If statements
            VariableDefinitionVisitor visitor = new VariableDefinitionVisitor();
            sqlFragment.Accept(visitor);
            List<DeclareVariableElement> variableDeclarations = visitor.VariableDeclarations;

            // Create problems for non builtin int variable found with a short name
            foreach (DeclareVariableElement variableDeclaration in variableDeclarations)
            {
                //ExcludedVariableTypesForNameLengthRule
                //MinimumVariableNameLength
                string typeRegex = @"(?i:^(" + String.Join(@"|", DMVSettings.ExcludedVariableTypesForNameLengthRule) + @")$)";
                string typename = variableDeclaration.DataType.Name.Identifiers[0].Value.ToLower();
                if (!Regex.IsMatch(typename, typeRegex,RegexOptions.None)
                     && variableDeclaration.VariableName.Value.Length <= DMVSettings.MinimumVariableNameLength // name includes leading @
                    )
                {
                    DataRuleProblem problem = new DataRuleProblem(this,
                                                String.Format(CultureInfo.CurrentCulture, this.RuleProperties.Description, SqlRuleUtils.GetElementName(sqlSchemaModel, sqlElement)),
                                                sqlElement);

                    SqlRuleUtils.UpdateProblemPosition(problem, variableDeclaration.StartOffset, variableDeclaration.FragmentLength);
                    problems.Add(problem);
                }
            }


            // check length of table variable names separately
            // visitor to get the declarations of table variables
            TableVariableDeclarationVisitor tableDeclarationVisitor = new TableVariableDeclarationVisitor();
            sqlFragment.Accept(tableDeclarationVisitor);
            List<Literal> tableVariableDeclarations = tableDeclarationVisitor.TableVariableDeclarations;
            // Create problems for table variables found with a short name
            foreach (Literal tableVariableDeclaration in tableVariableDeclarations)
            {
                if (!DMVSettings.ExcludedVariableTypesForNameLengthRuleIncludesTable
                     && tableVariableDeclaration.Value.Length <= DMVSettings.MinimumVariableNameLength // name includes leading @
                    )
                {
                    DataRuleProblem problem = new DataRuleProblem(this,
                                                String.Format(CultureInfo.CurrentCulture, this.RuleProperties.Description, SqlRuleUtils.GetElementName(sqlSchemaModel, sqlElement)),
                                                sqlElement);

                    SqlRuleUtils.UpdateProblemPosition(problem, tableVariableDeclaration.StartOffset, tableVariableDeclaration.FragmentLength);
                    problems.Add(problem);
                }
            }

            return problems;
        }

        #endregion    
 
    }
}